package cn.felord.common.kogito.dmn;

import org.drools.util.IoUtils;
import org.kie.dmn.api.core.DMNRuntime;
import org.kie.kogito.Application;
import org.kie.kogito.Config;
import org.kie.kogito.KogitoGAV;
import org.kie.kogito.StaticApplication;
import org.kie.kogito.config.ConfigBean;
import org.kie.kogito.decision.DecisionConfig;
import org.kie.kogito.dmn.DMNKogito;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import static org.kie.dmn.feel.util.ClassLoaderUtil.CAN_PLATFORM_CLASSLOAD;

/**
 * The type Dmn application builder.
 *
 * @author dax
 * @since 2023 /12/30
 */
public final class DmnApplicationBuilder {
    private static final PathMatchingResourcePatternResolver RESOLVER = new PathMatchingResourcePatternResolver();

    private DmnApplicationBuilder() {
    }

    /**
     * 从路径加载DMN
     *
     * @param config                  the config
     * @param decisionConfig          the decision config
     * @param dmnFilesLocationPattern the dmn files location pattern
     * @return the application
     * @throws IOException the io exception
     */
    public static Application dmnApplicationFromLocation(Config config, DecisionConfig decisionConfig, String dmnFilesLocationPattern) throws IOException {
        Resource[] resources = RESOLVER.getResources(dmnFilesLocationPattern);
        Reader[] readers = Arrays.stream(resources)
                .map(DmnApplicationBuilder::readResource)
                .toArray(InputStreamReader[]::new);
        return dmnApplication(config, decisionConfig, readers);
    }

    private static Application dmnApplication(Config config, DecisionConfig decisionConfig, Reader[] readers) {
        DMNRuntime dmnRuntime = DMNKogito.createGenericDMNRuntime(readers);
        decisionConfig.decisionEventListeners()
                .listeners()
                .forEach(dmnRuntime::addListener);
        KogitoGAV kogitoGAV = config.get(ConfigBean.class)
                .getGav()
                .orElse(KogitoGAV.EMPTY_GAV);
        SimpleDecisionModels engine = new SimpleDecisionModels(dmnRuntime, kogitoGAV);
        return new StaticApplication(config, engine);
    }

    /**
     * Read resource input stream reader.
     *
     * @param resource the resource
     * @return the input stream reader
     */
    protected static InputStreamReader readResource(Resource resource) {
        try {
            return readResource(resource.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Read resource input stream reader.
     *
     * @param stream the stream
     * @return the input stream reader
     */
    protected static InputStreamReader readResource(InputStream stream) {
        try {
            if (CAN_PLATFORM_CLASSLOAD) {
                return isrWithEncodingOrFallback(stream, "UTF-8");
            }
            byte[] bytes = IoUtils.readBytesFromInputStream(stream);
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            return isrWithEncodingOrFallback(byteArrayInputStream, "UTF-8");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static InputStreamReader isrWithEncodingOrFallback(InputStream stream, String encoding) {
        try {
            return new InputStreamReader(stream, encoding);
        } catch (UnsupportedEncodingException e) {
            return new InputStreamReader(stream);
        }
    }
}
